﻿# xls翻訳データをソースデータに変換。
#

package Xls2dat;
use strict;

use utf8;
BEGIN {
	unshift @INC, "..";
}
use Unicode::Japanese;
use Win32::OLE qw(in with CP_UTF8);
use Win32::OLE::Const 'Microsoft Excel';
use Cwd;

use conv_table;
use sort_simple;
use length;

$Win32::OLE::CP = CP_UTF8;

my $TRUE = 1;
my $FALSE = 0;
#my $USE_TRANS_PARAM = $FALSE;
my (
	$IF_LINE,
	$IF_LENGTH,
	$IF_FINISH,
	$IF_NONE,
	$IF_EOM,
	$IF_LIMIT,
	$IF_SCROLL,
	$IF_VAR_FOO
) = (0 .. 100);
my $TEMP_RES = "temp_res.txt";
my $SIMPLE_TEXT = "EME_Simpletext.xls";
my $EME_DEBUG = "EME_debug.xls";
my $PROJ_TRANS = "translations";
my $REL_MASTER_PATH = "translations";
my $REL_MASTER_SPEC_PATH = "translations/common";
my %CHECK_ID = (
	"check id" => \&check_id,
	"check num" => \&check_num,
	"trainer" => \&check_id
);
my $ERROR_LOG = "error_log.txt";
my $WARNING_LOG = "warning_log.txt";
my $DISUSE = "../extract_gomi/%s_disuse.txt";
my ($M_ID, $M_TYP, $M_SCL, $M_ARG, $M_TRJ, $M_TRS, $M_LEN, $M_FIL, $M_CMT);
my ($T_ID, $T_TRS, $T_MSJ, $T_MSG);
my ($M_ECOL, $T_ECOLN, $T_ECOLC);
my $VERSION = 0;
#if ($USE_TRANS_PARAM) {
#my ($P_ID, $P_PRM) = (0, 1);
#}

our $lang;
our $lang_dir;

my $excel;
my ($xls_master_path, $xls_trans_path);
my ($xls_master_spec_path, $xls_trans_spec_path);
my $happen_error;
my $full_path_trans_name;
my $full_path_master_name;
my $error_log;
my $warning_log;
my %disuse;
my $escape_mode;
!undef;

#-----------------------------------------------------------
# メイン。
#-----------------------------------------------------------
sub main {
	my %LANG_DIR = (
		English => "ENGLISH",
		French  => "FRANCE",
		German  => "GERMANY",
		Italian => "ITALY",
		Spanish => "SPAIN"
	);
	my %NORMAL_SEARCH_PROC = (
		array => \&search_msg_id_array,
		macro => \&search_msg_id_macro,
		asm => \&search_msg_id_asm
	);
	my %NORMAL_TYPE_CONV_PROC = (
		array => \&type_conv_array,
		macro => \&type_conv_dummy,
		asm => \&type_conv_asm
	);
	my %BRAILLE_SEARCH_PROC = (
		asm => \&search_msg_id_braille
	);
	my %BRAILLE_TYPE_CONV_PROC = (
		asm => \&type_conv_asm
	);
	my %SIMPLE_SEARCH_PROC = (
		macro => \&search_msg_id_macro,
	);
	my %SIMPLE_TYPE_CONV_PROC = (
		macro => \&type_conv_dummy,
	);
	my %CONV_FUNC = (
		conv_normal => \&convert_normal,
		conv_simple => \&convert_simple_text,
		conv_complex => \&convert_complex,
		conv_debug => \&convert_debug
	);
	my %LANG = (
		english => "English",
		french  => "French",
		german  => "German",
		italian => "Italian",
		spanish => "Spanish"
	);
	my @argv = @_;
	my $ini_data;
	my $temp;
	my ($simple_text_name, $simple_text);
	my @word_list;
	my @total_word_list;
	my @category_word_list;
	my $cnt;
	my $project_path;
	my $disuse;
	our @SHARP_NORMAL_TABLE;
	our @NORMAL_TABLE;
	our %SHARP_LANG_TABLE;
	our @BRAILLE_TABLE;
	our %BRAILLE_LANG_TABLE;
	our @SHARP_BRAILLE_TABLE;
	our %SHARP_LANG_BRAILLE_TABLE;

	if (@argv != 2) {
		usage();
		exit -1;
	}
	$lang = $LANG{$argv[0]};
	if (!defined($lang)) {
		usage();
		exit -1;
	}
	$escape_mode = ::get_escape_mode();
	$lang_dir = "LANG_$LANG_DIR{$lang}";
	$error_log = ::add_info_error($ERROR_LOG, $lang);
	$warning_log = ::add_info_warning($WARNING_LOG, $lang);
	if ($lang eq "English") {
		set_excel_col_english();
	} else {
		set_excel_col_other();
	}
	set_full_path_name_def();

	if (getcwd() =~ /^(.*)[\\\/]$PROJ_TRANS/) {
		$project_path = $1;
	}

	open LIST, "$argv[1]" or ::my_error("error: open $argv[1]");
	$xls_trans_path = <LIST>;
	$xls_trans_spec_path = <LIST>;
	chomp ($xls_trans_path, $xls_trans_spec_path);
	for my $i ($xls_trans_path, $xls_trans_spec_path) {
		if ($i =~ s/^\?//) {
			my $pre_path = $project_path;

			while ($i =~ s/^\/\.\.//) {
				unless ($pre_path =~ s/[\/\\][^\/\\]+$//) {
					::my_error("error: can not make rel-path($project_path)");
				}
			}
			$i = "$pre_path$i";
		}
	}

	$xls_master_path = "$project_path/$REL_MASTER_PATH/$lang";
	$xls_master_spec_path = "$project_path/$REL_MASTER_SPEC_PATH";

	$disuse = sprintf("$DISUSE", "\L$lang\E");
	open DISUSE, $disuse or ::my_error("error: open $disuse");
	while (<DISUSE>) {
		if (/^(\w+)\s+G/) {
			$disuse{$1} = $TRUE;
		}
	}
	close DISUSE;

	open_log(\*ERROR_LOG, $error_log);
	open_log(\*WARNING_LOG, $warning_log);

	$excel = open_excel();

	push_normal_table();
	# 部分文字列になっていないかチェック。
	check_word_subset(\@SHARP_NORMAL_TABLE);
	check_word_subset(\@NORMAL_TABLE);

	push(@BRAILLE_TABLE, @{$BRAILLE_LANG_TABLE{$lang}});
	push(@SHARP_BRAILLE_TABLE, @{$SHARP_LANG_BRAILLE_TABLE{$lang}});
	# 部分文字列になっていないかチェック。
	check_word_subset(\@BRAILLE_TABLE);
	check_word_subset(\@SHARP_BRAILLE_TABLE);

	# 準備。
	$temp = {};
	$temp->{'#'} = create_conv_sharp(\@SHARP_NORMAL_TABLE);
	$temp->{"\n"} = create_conv_no_eom(\&conv_newline);
	$temp->{';'} = \&conv_eom;
	for my $i (@NORMAL_TABLE) {
		create_conv($temp, $i);
	}
	$ini_data->{conv_normal}{normal}{conv} = $temp;
	$ini_data->{conv_normal}{normal}{search} = \%NORMAL_SEARCH_PROC;
	$ini_data->{conv_normal}{normal}{type_conv} = \%NORMAL_TYPE_CONV_PROC;
	$ini_data->{conv_normal}{normal}{use_type} = "use excel";

	$temp = {};
	$temp->{'#'} = create_conv_sharp(\@SHARP_BRAILLE_TABLE);
	$temp->{"\n"} = create_conv_no_eom(\&conv_newline);
	$temp->{';'} = \&conv_eom;
	for my $i (@BRAILLE_TABLE) {
		create_conv($temp, $i);
	}
	$ini_data->{conv_normal}{braille}{conv} = $temp;
	$ini_data->{conv_normal}{braille}{search} = \%BRAILLE_SEARCH_PROC;
	$ini_data->{conv_normal}{braille}{type_conv} = \%BRAILLE_TYPE_CONV_PROC;
	$ini_data->{conv_normal}{braille}{use_type} = "asm";

	$simple_text_name = $full_path_trans_name->($SIMPLE_TEXT);
	$simple_text = get_data_from_excel($simple_text_name, 2, "A", $T_ECOLN, $escape_mode);
	$temp = {};
	if (defined($simple_text)) {
		$cnt = 0;
		for my $i (reverse @{$simple_text}) {
			my ($word, $code, $category, $no);

			$i->[$T_TRS] =~ /^([^;]*);/;
			$word = $1;
			unless ($i->[$T_ID] =~ /^.{8}(\d{2}).{3}(\d+)$/) {
				::my_error("error: simple text format, $i->[$T_ID]");
			}
			($category, $no) = ($1, $2);
			if ($category < 0 || 0x7f < $category) {
				::my_error("error: overflow category, $i->[$T_ID]");
			}
			if ($no < 0 || 0x1ff < $no) {
				::my_error("error: overflow no, $i->[$T_ID]");
			}
			$code = ($category << 9) | $no;
			$temp->{$word} = sprintf("0x%04x,", $code);
			push @word_list, { word => $word, code => $code, no => $no };
			push @total_word_list, $cnt;
			push @{$category_word_list[$category]}, $cnt;
			$cnt++;
		}
	}
	$ini_data->{simple_text}{word} = $temp;
	$ini_data->{simple_text}{search} = \%SIMPLE_SEARCH_PROC;
	$ini_data->{simple_text}{type_conv} = \%SIMPLE_TYPE_CONV_PROC;

	while (<LIST>) {
		my $conv_func;

		chomp;
		if (/^#/) {
			next;
		}
		$conv_func = $CONV_FUNC{$_};
		if (!defined($conv_func)) {
			::my_error("error: list file format, $.");
		}
		$conv_func->($ini_data);
	}
	if (defined($simple_text)) {
		::common_logging("sorting simple word...\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);
		sort_simple(\@word_list, \@total_word_list, \@category_word_list);
		::common_logging("done!\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);
	} else {
		::print_console_progress("no $SIMPLE_TEXT, pass to sort simple-word\n", 1, 'bold');
	}

	$excel->Quit();
	$excel = undef;
	close LIST;
	close ERROR_LOG;
	close WARNING_LOG;
}

#-----------------------------------------------------------
# 使用方法。
#-----------------------------------------------------------
sub usage {
	my $com = $0;
	$com =~ s/.*\\//;

	print STDERR "\n";
	print STDERR "USAGE: $com <language> <list-file>\n";
	print STDERR "<language>:\n";
	print STDERR "    english\n";
	print STDERR "    french\n";
	print STDERR "    german\n";
	print STDERR "    italian\n";
	print STDERR "    spanish\n";
}

#-----------------------------------------------------------
# 通常のコンバート。
#-----------------------------------------------------------
sub convert_normal {
	my ($ini_data) = @_;
	my ($trans_name, $master_name, $type);
	my ($trans_name_path, $master_name_path);
#if ($USE_TRANS_PARAM) {
#	my ($trans_data, $param_data, $master_data);
#} else {
	my ($trans_data, $master_data);
#}
	my %trans;
#if ($USE_TRANS_PARAM) {
#	my %param;
#}
	my %out_data;
	my ($row, $proc, $length);
	our %LENGTH_TABLE;

	$_ = <LIST>;
	chomp;
	unless (/^\t([^\t]+)\t+([^\t]+)\t+([^\t]+)$/) {
		::my_error("error: list file format, $.");
	}
	($type, $trans_name, $master_name) = ($1, $2, $3);
	$trans_name_path = $full_path_trans_name->($trans_name);
	$master_name_path = $full_path_master_name->($master_name);
	$length = $LENGTH_TABLE{$master_name};

	::common_logging("[$trans_name]:\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);

	# エクセルファイルから、必要なデータを取得。
	::common_logging("get data from Excel...\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);
	$trans_data = get_data_from_excel($trans_name_path, 2, "A", $T_ECOLN, $escape_mode);
	if (!defined($trans_data)) {
		if ($trans_name ne $EME_DEBUG) {
			::print_console_progress("no $trans_name\n", 1, 'bold');
		} else {
			::common_logging("no $trans_name\n", [::OUT_BUILD_LOG()]);
		}
		return;
	}
#if ($USE_TRANS_PARAM) {
#	$param_data = get_data_from_excel($trans_name_path, 2, "A", "B", $FALSE, 2);
#}
	$master_data = get_data_from_excel($master_name_path, 2, "A", $M_ECOL);
	::common_logging("done!\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);

	# 翻訳データのテーブル作成。
	$row = 2;
	for my $i (@{$trans_data}) {
		check_id(\%trans, $i->[$T_ID], $row) = $i->[$T_TRS];
		$row++;
	}
	$trans_data = undef;

#if ($USE_TRANS_PARAM) {
#	# パラメータデータのテーブル作成。
#	$row = 2;
#	for my $i (@{$param_data}) {
#		while ($i->[$P_PRM] =~ /^#(.*)\/.*(?i:space):(\d+)$/gm) {
#			my ($var, $val) = ($1, $2);
#
#			$param{$i->[$P_ID]}{$var} = $val;
#		}
#		$row++;
#	}
#}

	# タイプの正当性チェック。
	$proc = $ini_data->{conv_normal}{$type};
	if (!defined($proc)) {
		::my_error("error: type = $type");
	}

	error_start("$master_name\n");

#if ($USE_TRANS_PARAM) {
#	trans_to_poke_string($proc->{conv}, $proc->{use_type}, \&check_id,
#		$master_data, $M_ID, \%trans, \%out_data, $length, \%param);
#} else {
	trans_to_poke_string($proc->{conv}, $proc->{use_type}, \&check_id,
		$master_data, $M_ID, \%trans, \%out_data, $length);
#}

	$master_data = undef;
	undef(%trans);

	output_to_src($proc->{search}, $proc->{type_conv}, \%out_data);
}

#-----------------------------------------------------------
# 複合ファイルのコンバート。
#-----------------------------------------------------------
sub convert_complex {
	my ($NAME, $INFO) = (0 .. 100);
	my %CHECK = (
		"trainer" => [ \&check_id, $T_MSJ, $M_TRJ ],
		"other" => [ \&check_num, $T_ID, $M_ID ]
	);
	my ($ini_data) = @_;
	my ($trans_name, $check, $check2);
	my @master_name;
	my $trans_name_path;
	my @master_name_path;
	my ($trans_data, $master_data);
	my %trans_n;
	my %trans_i;
	my %out_data;
	my ($row, $master);
	my @length;
	my $normal = $ini_data->{conv_normal}{normal};
	my ($check_id, $check_id2, $check_col, $comp_col);
	our %LENGTH_TABLE;

	$_ = <LIST>;
	chomp;
	unless (/^\t([^\t]+)\t+([^\t]+)\t+([^\t]+)\t+([^\t]+)\t+([^\t]+)$/) {
		::my_error("error: list file format, $.");
	}
	($trans_name, $check, $master_name[$NAME], $check2, $master_name[$INFO]) = ($1, $2, $3, $4, $5);
	$trans_name_path = $full_path_trans_name->($trans_name);
	$master_name_path[$NAME] = $full_path_master_name->($master_name[$NAME]);
	$master_name_path[$INFO] = $full_path_master_name->($master_name[$INFO]);
	$check_id = $CHECK_ID{$check};
	if (!defined($check_id)) {
		::my_error("error: list file format, check id = $check");
	}
	if (!defined($CHECK{$check2})) {
		::my_error("error: list file format, check id2 = $check2");
	}
	$length[$NAME] = $LENGTH_TABLE{$master_name[$NAME]};
	$length[$INFO] = $LENGTH_TABLE{$master_name[$INFO]};
	$check_id2 = $CHECK{$check2}[0];
	$check_col = $CHECK{$check2}[1];
	$comp_col = $CHECK{$check2}[2];

	::common_logging("[$trans_name]:\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);

	# エクセルファイルから、必要なデータを取得。
	::common_logging("get data from Excel...\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);
	$trans_data = get_data_from_excel($trans_name_path, 2, "A", $T_ECOLC, $escape_mode);
	if (!defined($trans_data)) {
		::print_console_progress("no $trans_name\n", 1, 'bold');
		return;
	}
	$master_data = get_data_from_excel($master_name_path[$NAME], 2, "A", $M_ECOL);
	::common_logging("done!\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);

	# 翻訳データのテーブル作成。
	# 逆順にするのは、トレーナタイプの一致チェックが先頭から最初に出たもの、
	# となっているため。
	$row = @{$trans_data} + 2;
	for my $i (reverse @{$trans_data}) {
		$check_id->(\%trans_n, $i->[$T_ID], $row) = $i->[$T_TRS];
		$check_id2->(\%trans_i, $i->[$check_col], $row) = $i->[$T_MSG];
		$row--;
	}
	$trans_data = undef;

	error_start("$master_name[$NAME]\n");

#if ($USE_TRANS_PARAM) {
#	trans_to_poke_string($normal->{conv}, $normal->{use_type}, $check_id,
#		$master_data, $M_ID, \%trans_n, \%out_data, $length[$NAME], {});
#} else {
	trans_to_poke_string($normal->{conv}, $normal->{use_type}, $check_id,
		$master_data, $M_ID, \%trans_n, \%out_data, $length[$NAME]);
#}

	$master_data = undef;
	undef(%trans_n);

	output_to_src($normal->{search}, $normal->{type_conv}, \%out_data);

	undef(%out_data);

	# エクセルファイルから、必要なデータを取得。
	::common_logging("get data from Excel...\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);
	$master_data = get_data_from_excel($master_name_path[$INFO], 2, "A", $M_ECOL);
	::common_logging("done!\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);

	error_start("$master_name[$INFO]\n");

#if ($USE_TRANS_PARAM) {
#	trans_to_poke_string($normal->{conv}, $normal->{use_type}, $check_id2,
#		$master_data, $comp_col, \%trans_i, \%out_data, $length[$INFO], {});
#} else {
	trans_to_poke_string($normal->{conv}, $normal->{use_type}, $check_id2,
		$master_data, $comp_col, \%trans_i, \%out_data, $length[$INFO]);
#}

	$master_data = undef;
	undef(%trans_i);

	output_to_src($normal->{search}, $normal->{type_conv}, \%out_data);
}

#-----------------------------------------------------------
# デバッグメッセージ。
# ※ これだけ、英語版と同じフォーマットを全言語で利用。
#-----------------------------------------------------------
sub convert_debug {
	my ($ini_data) = @_;

	push_normal_rmv_lang_add_eng($ini_data);
	set_full_path_name_spec();
	set_excel_col_english();

	convert_normal($ini_data);

	if ($lang ne "English") {
		set_excel_col_other();
	}
	set_full_path_name_def();
	push_normal_rmv_eng_add_lang($ini_data);
}

#-----------------------------------------------------------
# シンプルテキストのコンバート。
#-----------------------------------------------------------
sub convert_simple_text {
	my ($ini_data) = @_;
	my ($trans_name, $master_name);
	my ($trans_name_path, $master_name_path);
	my ($trans_data, $master_data);
	my %trans;
	my %out_data;
	my $row;
	my $simple_text = $ini_data->{simple_text};
	my $word = $simple_text->{word};

	$_ = <LIST>;
	chomp;
	unless (/^\t([^\t]+)\t+([^\t]+)$/) {
		::my_error("error: list file format, $.");
	}
	($trans_name, $master_name) = ($1, $2);
	$trans_name_path = $full_path_trans_name->($trans_name);
	$master_name_path = $full_path_master_name->($master_name);

	::common_logging("[$trans_name]:\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);

	# エクセルファイルから、必要なデータを取得。
	::common_logging("get data from Excel...\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);
	$trans_data = get_data_from_excel($trans_name_path, 2, "A", $T_ECOLN, $escape_mode);
	if (!defined($trans_data)) {
		::print_console_progress("no $trans_name\n", 1, 'bold');
		return;
	}
	$master_data = get_data_from_excel($master_name_path, 2, "A", $M_ECOL);
	::common_logging("done!\n", [::OUT_BUILD_LOG(), ::OUT_STDOUT()]);

	error_start("$master_name\n");

	# 翻訳データを簡易会話ワードを使って置き換えて、翻訳データを作成。
	$row = 2;
	for my $i (@{$trans_data}) {
		my @word;
		my $simple;

		::check_cancel();

		$happen_error = $FALSE;
		$i->[$T_TRS] =~ s/\/ +/\//g;
		$i->[$T_TRS] =~ s/ +\//\//g;
		$i->[$T_TRS] =~ s/^ +//g;
		$i->[$T_TRS] =~ s/ +$//g;
		@word = split /\//, $i->[$T_TRS];
		$simple = "";
		for my $j (@word) {
			my $value;

			if ($j eq "NULL") {
				$simple .= "0xffff,";
				next;
			}
			$value = $word->{$j};
			if (!defined($value)) {
				error_log("${j}は知らないワードです。", $row, $i->[$T_ID]);
				$value = "0xffff,";
			}
			$simple .= $value;
		}
		$trans{$i->[$T_ID]} = [ $simple, $happen_error ];
	} continue {
		$row++;
	}
	$trans_data = undef;

	# 出力データ生成。
	$row = 2;
	for my $i (@{$master_data}) {
		my $trans = $trans{$i->[$M_ID]};

		::check_cancel();

		if (check_disuse($i->[$M_ID], $i->[$M_CMT], $row)) {
			# 処理しない。
			next;
		}
		if (!defined($trans)) {
			error_log("翻訳データがありません。", $row, $i->[$M_ID]);
			next;
		}
		$happen_error = $trans->[1];
		if ($i->[$M_LEN] =~ /^(\d+)\+0$/) {
			my $length = ($trans->[0] =~ tr/,//);

			if ($length != $1) {
				error_log("length:文字数が一致しません。", $row, $i->[$M_ID]);
			}
		} else {
			error_log("length:$i->[$M_LEN]は想定されていません。", $row, $i->[$M_ID]);
		}
		if (!$happen_error) {
			$i->[$M_FIL] =~ s/LANG_\w+/$lang_dir/;
			$out_data{lc($i->[$M_FIL])}{macro}{$i->[$M_ID]} = [ $trans->[0], $row ];
		}
	} continue {
		$row++;
	}

	$master_data = undef;
	undef(%trans);

	output_to_src($simple_text->{search}, $simple_text->{type_conv}, \%out_data);
}

#-----------------------------------------------------------
# ポケモン文字列へ変換。
#-----------------------------------------------------------
sub trans_to_poke_string {
	my %TYPE = ( array => $TRUE, macro => $TRUE, asm => $TRUE );
#if ($USE_TRANS_PARAM) {
#	my %CODE_LIST = (C_STRUT_ => $TRUE);
#	my ($proc, $type, $check_id, $master_data, $comp_id,
#		$trans_t, $out_data, $length_table, $param) = @_;
#} else {
	my ($proc, $type, $check_id, $master_data, $comp_id,
		$trans_t, $out_data, $length_table) = @_;
#}
	my $use_excel = ($type eq "use excel") ? $TRUE : $FALSE;
	my $row;

	$row = 2;
	ROW: for my $i (@{$master_data}) {
		my ($trans, $c, $out);
		my @info;

		::check_cancel();
		$happen_error = $FALSE;
		if (check_disuse($i->[$M_ID], $i->[$M_CMT], $row)) {
			# 処理しない。
			next;
		}
		$trans = $check_id->($trans_t, $i->[$comp_id], $row);
		if (!defined($trans)) {
			# 翻訳データがない場合には、マスターファイルのデータをそのまま使う(仕様)。
			$trans = $i->[$M_TRS];
		}
		if (!defined($TYPE{$i->[$M_TYP]})) {
			error_log("type:$i->[$M_TYP]は知りません。", $row, $i->[$M_ID]);
			next;
		}

		# 初期設定。
		$info[$IF_LINE] = 1;
		$info[$IF_LENGTH] = 0;
		$info[$IF_FINISH] = $FALSE;
		$info[$IF_NONE] = $TRUE;
		$info[$IF_EOM] = $FALSE;
		$info[$IF_LIMIT] = $i->[$M_LEN];
		if ($i->[$M_SCL] eq "true") {
			$info[$IF_SCROLL] = $TRUE;
		} elsif ($i->[$M_SCL] eq "false") {
			$info[$IF_SCROLL] = $FALSE;
		} else {
			error_log("scroll:$i->[$M_SCL]は知りません。", $row, $i->[$M_ID]);
			next;
		}
		while ($i->[$M_ARG] =~ /^#(.*)::(.*)$/gm) {
			my ($var, $rep) = ($1, $2);

			if ($var =~ /^var[0-2]$/) {
				error_log("type:${var}は予約語です。", $row, $i->[$M_ID]);
				next;
			}
#if ($USE_TRANS_PARAM) {
#			if ($rep =~ /^CTRL_,(\w+),(\d+)$/) {
#				my ($code, $val) = ($1, $2);
#
#				if (defined($CODE_LIST{$code})) {
#					my ($t_val) = $param->{$i->[$M_ID]}{$var};
#
#					if (!defined($t_val)) {
#						error_log("type:${var}のピクセル値定義が翻訳ファイルにありません。", $row, $i->[$M_ID]);
#					} elsif ($val != $t_val) {
#						warning_log("type:ピクセル値定義がマスターと翻訳で異なっています。念のため報告です。", $row, $i->[$M_ID]);
#					} else {
#						$rep = "CTRL_,$code,$t_val";
#					}
#				}
#			}
#}
			$info[$IF_VAR_FOO]{$var} = "$rep,";
		}
		for my $j (0 .. 2) {
			$info[$IF_VAR_FOO]{"var$j"} = "I_MSG_,STR_TEMP_BUF$j,";
		}

		# 変換。
		while (($c = substr($trans, 0, 1, "")) ne "") {
			my $sub;

			$sub = $proc->{$c};
			if (!defined($sub)) {
				error_log("($c)こんな字、変換できません。", $row, $i->[$M_ID]);
				next;
			}
			$out .= $sub->(\$trans, \@info, $row, $i->[$M_ID]);
			if ($info[$IF_FINISH]) {
				next ROW;
			}
		}
		if (!$info[$IF_EOM]) {
			error_log("EOMがないぞ。", $row, $i->[$M_ID]);
		} elsif ($info[$IF_NONE]) {
			error_log("EOMしかないぞ。", $row, $i->[$M_ID]);
		}
		if ($info[$IF_LIMIT] ne "-1") {
			my ($limit, $eom_cut, $error);

			$error = $TRUE;
			if (!defined($length_table)) {
				error_log("length:テーブルに設定がありません。", $row, $i->[$M_ID]);
			} elsif ($info[$IF_LIMIT] =~ /^(\d+)\+([01])$/) {
				$limit = $1;
				$eom_cut = ($2 == 0) ? "yes" : "no";
				for my $j (@{$length_table}) {
					if ($i->[$M_ID] =~ /$j->{reg}/) {
						if ($limit == $j->{limit} && $eom_cut eq $j->{eom_cut}) {
							$eom_cut = ($eom_cut eq "yes") ? $TRUE : $FALSE;
							$error = $FALSE;
						}
					}
				}
				if ($error) {
					error_log("length:テーブルに一致するIDがありません。", $row, $i->[$M_ID]);
				}
			} else {
				error_log("length:$info[$IF_LIMIT]は想定されていません。", $row, $i->[$M_ID]);
			}
			if (!$error) {
				if ($limit < $info[$IF_LENGTH]) {
					error_log("文字数制限オーバーです。", $row, $i->[$M_ID]);
				} elsif ($info[$IF_LENGTH] == $limit && $eom_cut) {
					$out =~ s/EOM_,$//;
					warning_log("EOMカットしました。", $row, $i->[$M_ID]);
				}
			}
		}

		# 変換データの保存。
		if (!$happen_error) {
			$i->[$M_FIL] =~ s/LANG_\w+/$lang_dir/;
			if ($use_excel) {
				$out_data->{lc($i->[$M_FIL])}{$i->[$M_TYP]}{$i->[$M_ID]} = [ $out, $row ];
			} else {
				$out_data->{lc($i->[$M_FIL])}{$type}{$i->[$M_ID]} = [ $out, $row ];
			}
		}
	} continue {
		$row++;
	}
}

#-----------------------------------------------------------
# メッセージIDをそのもので引く。
#-----------------------------------------------------------
sub check_id : lvalue {
	my ($trans, $id) = @_;

	$trans->{$id};
}

#-----------------------------------------------------------
# メッセージIDを番号のみで引く。
#-----------------------------------------------------------
sub check_num : lvalue {
	my ($trans, $id, $row) = @_;

	unless ($id =~ /(\w{3})$/) {
		error_log("翻訳のIDの最後の3桁が文字じゃねーぞ。", $row, $id);
		return undef;
	}

	$trans->{$1};
}

#-----------------------------------------------------------
# 最終出力。
#-----------------------------------------------------------
sub output_to_src {
	my ($search_proc, $type_conv_proc, $out_data) = @_;

	for my $i (keys %{$out_data}) {
		my $path = rel_path_from($i);
		my $out_file;

		open OUTPUT, $path or ::my_error("error: open $path");
		$out_file = Unicode::Japanese->new(join("", <OUTPUT>), 'sjis')->getu;
		close OUTPUT;

		for my $j (keys %{$out_data->{$i}}) {
			my %msg_list;
			my @msg_exist;
			my $out_list;
			my $out_new;
			my $prev_pos;

			::check_cancel();

			# 出力ファイルの準備。
			$search_proc->{$j}(\$out_file, \%msg_list, \@msg_exist, $path, $j);

			# メッセージの存在チェックとリスト準備。
			$out_list = $out_data->{$i}{$j};
			for my $k (keys %{$out_list}) {
				if (!defined($msg_list{$k})) {
					error_log("${i}にメッセージIDが見つかりません。", $out_list->{$k}[1], $k);
				}
				$msg_list{$k}[0] = $out_list->{$k}[0];
			}

			# メッセージを反映した出力内容を準備。
			$out_new = "";
			$prev_pos = 0;
			for my $k (@msg_exist) {
				my $out;

				$out = $k->[0];
				if (!defined($out)) {
					next;
				}
				$out_new .= substr($out_file, $prev_pos, $k->[1] - $prev_pos);
				$out_new .= $type_conv_proc->{$j}($out);
				$prev_pos = $k->[2];
			}
			$out_new .= substr($out_file, $prev_pos);
			$out_file = $out_new;
		}
		output($path, $out_file);
	}
}

#-----------------------------------------------------------
# 各ワードが他のワードの部分文字列になっていないか。
#-----------------------------------------------------------
sub check_word_subset {
	my ($table) = @_;

	for my $i (@{$table}) {
		my $trans_i = $i->{trans};

		for my $j (@{$table}) {
			my $trans_j = $j->{trans};

			if ($trans_i eq $trans_j) {
				next;
			}
			if ($trans_i eq substr($trans_j, 0, length($trans_i))) {
				::my_error("error: $trans_i is a part of $trans_j!");
			}
		}
	}
}

#-----------------------------------------------------------
# array形式のmsg_id抽出。
#-----------------------------------------------------------
sub search_msg_id_array {
	my ($out_file, $msg_list, $msg_exist, $path, $type) = @_;

	while (${$out_file} =~ /^(?:(?:static\s+)|(?:ALIGN4\s+))?const\s+u8\s+(?:ALIGN4\s+)?(\w+)\s*\[\s*\]\s*=\s*\{/mg) {
		my $msg_id = $1;
		my $before_info = $msg_list->{$msg_id};
		my $out;

		$msg_list->{$msg_id}[1] = pos(${$out_file});
		if (${$out_file} !~ /};/mg) {
			::my_error("error: no end, $path, $msg_id, $type");
		}
		$msg_list->{$msg_id}[2] = pos(${$out_file}) - 2;
		push(@{$msg_exist}, $msg_list->{$msg_id});
		if (defined($before_info)) {
			error_log("$path: メッセージIDの多重定義です。", 0, $msg_id);
		}
	}
}

#-----------------------------------------------------------
# macro形式のmsg_id抽出。
#-----------------------------------------------------------
sub search_msg_id_macro {
	my ($out_file, $msg_list, $msg_exist, $path, $type) = @_;

	while (${$out_file} =~ /^#define\s+(\w+)\s+{/mg) {
		my $msg_id = $1;
		my $before_info = $msg_list->{$msg_id};
		my $out;

		$msg_list->{$msg_id}[1] = pos(${$out_file});
		if (${$out_file} !~ /}/mg) {
			::my_error("error: no end, $path, $msg_id, $type");
		}
		$msg_list->{$msg_id}[2] = pos(${$out_file}) - 1;
		push(@{$msg_exist}, $msg_list->{$msg_id});
		if (defined($before_info)) {
			error_log("$path: メッセージIDの多重定義です。", 0, $msg_id);
		}
	}
}

#-----------------------------------------------------------
# asm形式のmsg_id抽出。
#-----------------------------------------------------------
sub search_msg_id_asm {
	my ($out_file, $msg_list, $msg_exist, $path, $type) = @_;

	while (${$out_file} =~ /^(\w+):(.*)$/mg) {
		my $msg_id = $1;
		my $before_info = $msg_list->{$msg_id};
		my $out;

		$msg_list->{$msg_id}[1] = pos(${$out_file}) + 1;
		if (${$out_file} !~ /EOM_$/mg) {
			::my_error("error: no end, $path, $msg_id, $type");
		}
		$msg_list->{$msg_id}[2] = pos(${$out_file});
		push(@{$msg_exist}, $msg_list->{$msg_id});
		if (defined($before_info)) {
			error_log("$path: メッセージIDの多重定義です。", 0, $msg_id);
		}
	}
}

#-----------------------------------------------------------
# braille形式のmsg_id抽出。
#-----------------------------------------------------------
sub search_msg_id_braille {
	my ($out_file, $msg_list, $msg_exist, $path, $type) = @_;

	while (${$out_file} =~ /^(\w+):(.*)$/mg) {
		my $msg_id = $1;
		my $before_info = $msg_list->{$msg_id};
		my $out;

		if (${$out_file} !~ /^\/\/ localized id=$msg_id: begin$/mg) {
			::my_error("error: no true start, $path, $msg_id, $type");
		}
		$msg_list->{$msg_id}[1] = pos(${$out_file}) + 1;
		if (${$out_file} !~ /EOM_$/mg) {
			::my_error("error: no end, $path, $msg_id, $type");
		}
		$msg_list->{$msg_id}[2] = pos(${$out_file});
		push(@{$msg_exist}, $msg_list->{$msg_id});
		if (defined($before_info)) {
			error_log("$path: メッセージIDの多重定義です。", 0, $msg_id);
		}
	}
}

#-----------------------------------------------------------
# array形式のメッセージ変換。
#-----------------------------------------------------------
sub type_conv_array {
	my ($out) = @_;

	$out =~ s/^/\t/gm;
	return "\n$out\n";
}

#-----------------------------------------------------------
# asm形式のメッセージ変換。
#-----------------------------------------------------------
sub type_conv_asm {
	my ($out) = @_;

	$out =~ s/,$//gm;
	$out =~ s/^/\t.byte\t/gm;
	return $out;
}

#-----------------------------------------------------------
# ダミーメッセージ変換。
#-----------------------------------------------------------
sub type_conv_dummy {
	my ($out) = @_;

	return $out;
}

#-----------------------------------------------------------
# EOM以外の変換関数は、必ずこの関数でラップする。
#-----------------------------------------------------------
sub create_conv_no_eom {
	my ($sub_true) = @_;
	my $sub;

	$sub = sub {
		my ($trans, $info, $row, $msg_id) = @_;

		if ($info->[$IF_EOM]) {
			error_log("EOMの後に字書いたらあかん。", $row, $msg_id);
			$info->[$IF_FINISH] = $TRUE;
			return "";
		}
		$info->[$IF_NONE] = $FALSE;
		return &$sub_true;
	};
	return $sub;
}

#-----------------------------------------------------------
# 変換関数の生成。
#-----------------------------------------------------------
sub create_conv {
	my ($proc, $dat) = @_;
	my $sub;
	my $trans;

	$trans = $dat->{trans};
	$sub = create_conv_normal($dat->{conv});
	$proc->{$trans} = create_conv_no_eom($sub);

	return length($trans);
}

#-----------------------------------------------------------
# 通常の変換関数の生成。
#-----------------------------------------------------------
sub create_conv_normal {
	my ($conv_c) = @_;
	my $length;
	my $sub;

	$length = $conv_c =~ tr/,//;

	$sub = sub {
		my ($trans, $info, $row, $msg_id) = @_;

		# 長さ制限の使われ方からして、通常の文字をカウントすればOK。
		$info->[$IF_LENGTH] += $length;
		return $conv_c;
	};
	return $sub;
}

#-----------------------------------------------------------
# 改行の変換関数。
#-----------------------------------------------------------
sub conv_newline {
	my ($trans, $info) = @_;
	my $conv_c;

	if (substr(${$trans}, 0, 1) eq "\n") {
		substr(${$trans}, 0, 1, "");
		if ($info->[$IF_SCROLL]) {
			$conv_c = "NORMAL_WAIT_,\n";
			$info->[$IF_LINE] = 1;
		} else {
			$conv_c = "CR_,\nCR_,\n";
			$info->[$IF_LINE] += 2;
		}
	} else {
		if ($info->[$IF_LINE] == 1 || !$info->[$IF_SCROLL]) {
			$conv_c = "CR_,\n";
		} else {
			$conv_c = "SCROLL_WAIT_,\n";
		}
		$info->[$IF_LINE]++;
	}
	return $conv_c;
}

#-----------------------------------------------------------
# EOMの変換関数。
#-----------------------------------------------------------
sub conv_eom {
	my ($trans, $info) = @_;

	$info->[$IF_EOM] = $TRUE;
	return "EOM_,";
}

#-----------------------------------------------------------
# シャープ記号の変換関数の生成。
#-----------------------------------------------------------
sub create_conv_sharp {
	my ($ini) = @_;
	my %proc;
	my $sharp_max_len;
	my $sub;
	my @var_foo = ("var", "foo");

	$sharp_max_len = 0;
	for my $i (@{$ini}) {
		my $len;

		$len = create_conv(\%proc, $i);
		if ($sharp_max_len < $len) {
			$sharp_max_len = $len;
		}
	}
	for my $i (@var_foo) {
		my $len;

		$len = length $i;
		if ($sharp_max_len < $len) {
			$sharp_max_len = $len;
		}
		$proc{$i} = create_conv_var_foo($i);
	}

	$sub = sub {
		my ($trans, $info, $row, $msg_id) = @_;
		my $str;

		for my $i (1 .. $sharp_max_len) {
			my $c;

			$c = substr(${$trans}, 0, 1, "");
			if ($c eq "") {
				last;
			}
			$str .= $c;
			$sub = $proc{$str};
			if (defined($sub)) {
				return &$sub;
			}
		}

		error_log("知らない#は使っちゃダメ。", $row, $msg_id);
		return "";
	};
	return $sub;
}

#-----------------------------------------------------------
# var,fooの変換関数の生成。
#-----------------------------------------------------------
sub create_conv_var_foo {
	my ($n) = @_;
	my $sub;

	$sub = sub {
		my ($trans, $info, $row, $msg_id) = @_;
		my ($c, $conv_c);

		$c = substr(${$trans}, 0, 1, "");
		if ($c eq "" || !defined($conv_c = $info->[$IF_VAR_FOO]{"$n$c"})) {
			error_log("$n${c}は無い。", $row, $msg_id);
			return "";
		}
		return $conv_c;
	};
	return create_conv_no_eom($sub);
}

#-----------------------------------------------------------
# エラーログファイルヘッダ。
#-----------------------------------------------------------
sub error_start {
	my ($name) = @_;
	my $name_utf8 = Unicode::Japanese->new($name)->utf8;

	print ERROR_LOG $name_utf8;
	print WARNING_LOG $name_utf8;
}

#-----------------------------------------------------------
# エラーログ出力。
#-----------------------------------------------------------
sub error_log {
	my ($str, $row, $msg_id) = @_;
	my $text;

	$text = log_format($str, $row, $msg_id);
	print ERROR_LOG Unicode::Japanese->new($text)->utf8;
	$happen_error = $TRUE;
	::happen_error();
}

#-----------------------------------------------------------
# ワーニングログ出力。
#-----------------------------------------------------------
sub warning_log {
	my ($str, $row, $msg_id) = @_;
	my $text;

	$text = log_format($str, $row, $msg_id);
	print WARNING_LOG Unicode::Japanese->new($text)->utf8;
}

#-----------------------------------------------------------
# ログをフォーマットする。
#-----------------------------------------------------------
sub log_format {
	my ($str, $row, $msg_id) = @_;
	my $res;
	my $left;

	$left = 5 - length($row);
	if ($left < 0) {
		$left = 0;
	}
	$res .= "$row: " . (" " x $left);

	$left = 40 - length($msg_id);
	if ($left < 0) {
		$left = 0;
	}
	$res .= "$msg_id: " . (" " x $left);
	$res .= "$str\n";

	return $res;
}

#-----------------------------------------------------------
# エクセルオープン。
#-----------------------------------------------------------
sub open_excel {
	my $excel;

	$excel = Win32::OLE->new('Excel.Application');
	if (!defined($excel)) {
		::my_error("error: start Excel");
	}
	return $excel;
}

#-----------------------------------------------------------
# エクセルブック取得。
#-----------------------------------------------------------
sub get_book {
	my ($excel, $file_name, $escape_error) = @_;
	my $book;

	$book = $excel->Workbooks->Open($file_name);
	if (!defined($book)) {
		if (!$escape_error) {
			::my_error("error: open $file_name");
		}
	}
	return $book;
}

#-----------------------------------------------------------
# エクセルシート取得。
#-----------------------------------------------------------
sub get_sheet {
	my ($book, $sheet_num) = @_;
	my $sheet;

	$sheet = $book->Worksheets($sheet_num);
	if (!defined($sheet)) {
		::my_error("error: get sheet $sheet_num");
	}
	return $sheet;
}

#-----------------------------------------------------------
# エクセルからデータ取得。
#-----------------------------------------------------------
sub get_data_from_excel {
#if ($USE_TRANS_PARAM) {
#	my ($book_name, $start_row, $start_col, $end_col, $escape_error, $sheet_no) = @_;
#} else {
	my ($book_name, $start_row, $start_col, $end_col, $escape_error) = @_;
#}
	my ($book, $sheet, $range, $data);

#if ($USE_TRANS_PARAM) {
#	if (!defined($sheet_no)) {
#		$sheet_no = 1;
#	}
#}
	$book = get_book($excel, $book_name, $escape_error);
	if ($escape_error && !defined($book)) {
		return;
	}
#if ($USE_TRANS_PARAM) {
#	$sheet = get_sheet($book, $sheet_no);
#} else {
	$sheet = get_sheet($book, 1);
#}
	$range = $sheet->Range("A65536")->End(xlUp);
	if ($range->{Row} < $start_row) {
		$data = [];
	} else {
		$data = $sheet->Range("$start_col$start_row:$end_col$range->{Row}")->{Value};
	}
	$book->Close();

	return $data;
}

#-----------------------------------------------------------
# 翻訳ファイル絶対パス取得。
#-----------------------------------------------------------
sub full_path_trans_name {
	my ($trans_name) = @_;

	return sprintf("$xls_trans_path/$trans_name", $lang);
}

#-----------------------------------------------------------
# マスターファイル絶対パス取得。
#-----------------------------------------------------------
sub full_path_master_name {
	my ($master_name) = @_;

	return "$xls_master_path/$master_name";
}

#-----------------------------------------------------------
# 特殊翻訳ファイル絶対パス取得。
#-----------------------------------------------------------
sub full_path_trans_spec_name {
	my ($name) = @_;

	return "$xls_trans_spec_path/$name";
}

#-----------------------------------------------------------
# 特殊マスターファイル絶対パス取得。
#-----------------------------------------------------------
sub full_path_master_spec_name {
	my ($name) = @_;

	return "$xls_master_spec_path/$name";
}

#-----------------------------------------------------------
# デフォルトフルパス取得関数設定。
#-----------------------------------------------------------
sub set_full_path_name_def {
	$full_path_trans_name = \&full_path_trans_name;
	$full_path_master_name = \&full_path_master_name;
}

#-----------------------------------------------------------
# 特殊フルパス取得関数設定。
#-----------------------------------------------------------
sub set_full_path_name_spec {
	$full_path_trans_name = \&full_path_trans_spec_name;
	$full_path_master_name = \&full_path_master_spec_name;
}

#-----------------------------------------------------------
# 本ツールからの相対パスに変換。
#-----------------------------------------------------------
sub rel_path_from {
	my ($file) = @_;

	return "../../../$file";
}

#-----------------------------------------------------------
# ファイル出力処理。
#-----------------------------------------------------------
sub output {
	my ($path, $out_file) = @_;

	if (1) {
		open OUTPUT, ">$path" or ::my_error("error: open $path");
		print OUTPUT Unicode::Japanese->new($out_file)->sjis;
		close OUTPUT;
	} else {
		my $out_file_name;

		$path =~ /\.\.\/\.\.\/\.\.\/(.*)$/;
		$out_file_name = "res/$1";
		open OUTPUT, ">res/$1" or ::my_error("error: open res/$1");
		print OUTPUT Unicode::Japanese->new($out_file)->sjis;
		close OUTPUT;
	}
}

#-----------------------------------------------------------
# 終了。
#-----------------------------------------------------------
sub finish {
	if (defined($excel)) {
		$excel->Quit();
		$excel = undef;
	}
}

#-----------------------------------------------------------
# 英語用エクセルの列情報設定。
#-----------------------------------------------------------
sub set_excel_col_english {
	($M_ID, $M_TYP, $M_SCL, $M_ARG, $M_TRJ, $M_TRS, $M_LEN, $M_FIL, $M_CMT)
		= (0 .. 100);
	$M_ECOL = "I";
	($T_ID, undef, undef, $T_TRS, $T_MSJ, $T_MSG) = (0 .. 100);
	($T_ECOLN, $T_ECOLC) = ("D", "F");
}

#-----------------------------------------------------------
# 英語以外用エクセルの列情報設定。
#-----------------------------------------------------------
sub set_excel_col_other {
	($M_ID, $M_TYP, $M_SCL, $M_ARG, $M_TRJ, undef, $M_TRS, $M_LEN, $M_FIL, $M_CMT)
		= (0 .. 100);
	$M_ECOL = "J";
	($T_ID, undef, undef, undef, $T_TRS, $T_MSJ, undef, $T_MSG) = (0 .. 100);
	($T_ECOLN, $T_ECOLC) = ("E", "H");
}

#-----------------------------------------------------------
# 言語毎のシャープ文字処理追加。
#-----------------------------------------------------------
sub push_normal_table {
	our @SHARP_NORMAL_TABLE;
	our %SHARP_LANG_TABLE;

	push(@SHARP_NORMAL_TABLE, @{$SHARP_LANG_TABLE{$lang}});
}

#-----------------------------------------------------------
# 言語毎のシャープ文字処理削除＋英語用追加。
#-----------------------------------------------------------
sub push_normal_rmv_lang_add_eng {
	my ($ini_data) = @_;
	our @SHARP_NORMAL_TABLE;
	our %SHARP_LANG_TABLE;

	splice(@SHARP_NORMAL_TABLE, (@SHARP_NORMAL_TABLE - @{$SHARP_LANG_TABLE{$lang}}));
	push(@SHARP_NORMAL_TABLE, @{$SHARP_LANG_TABLE{English}});
	$ini_data->{conv_normal}{normal}{conv}{'#'} = create_conv_sharp(\@SHARP_NORMAL_TABLE);
}

#-----------------------------------------------------------
# 英語用のシャープ文字処理削除＋言語毎用追加。
#-----------------------------------------------------------
sub push_normal_rmv_eng_add_lang {
	my ($ini_data) = @_;
	our @SHARP_NORMAL_TABLE;
	our %SHARP_LANG_TABLE;

	splice(@SHARP_NORMAL_TABLE, (@SHARP_NORMAL_TABLE - @{$SHARP_LANG_TABLE{English}}));
	push(@SHARP_NORMAL_TABLE, @{$SHARP_LANG_TABLE{$lang}});
	$ini_data->{conv_normal}{normal}{conv}{'#'} = create_conv_sharp(\@SHARP_NORMAL_TABLE);
}

#-----------------------------------------------------------
# disuseファイルをチェックする。
#-----------------------------------------------------------
sub check_disuse {
	my ($id, $cmt, $row) = @_;
	my ($res, $key);

	$res = $cmt =~ /^(#(?:EXC|GOMI))\]$/;
	$key = $1;
	if ($disuse{$id}) {
		if (!$res) {
			warning_log("disuseのためスキップします(GOMI/EXCされていません)。", $row, $id);
			return $TRUE;
		}
	}
	if ($res) {
		warning_log("${key}のためスキップします。", $row, $id);
		return $TRUE;
	}

	return $FALSE;
}

#-----------------------------------------------------------
# バージョン取得。
#-----------------------------------------------------------
sub version {
	return $VERSION;
}

#-----------------------------------------------------------
# ログファイルを開く。
#-----------------------------------------------------------
sub open_log {
	my ($glob, $file) = @_;

	open $glob, ">$file" or ::my_error("error: open $file");
	print $glob "\x{ef}\x{bb}\x{bf}";
}
